/*
 * Copyright (c) 2022-2023 Termony Co., Ltd. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this list of
 *    conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
 *    of conditions and the following disclaimer in the documentation and/or other materials
 *    provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
 *    to endorse or promote products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "los_arch_gic.h"
#include "los_arch_cpu.h"
#include "los_interrupt.h"
#include "los_debug.h"

#if (LOSCFG_PLATFORM_GIC_VERSION == 3)

static struct {
    UINT32 cpuId; /* The ID of the CPU running the LiteOS-M. */
    UINT16 maxIrq; /* The maximum number of interrupts supported by the processor. */
    UINT16 priOft; /* Implemented priority bit offset. */
    struct {
        UINT32 offset; /* the first interrupt index in the vector table */
        UINT32 distBase; /* the base address of the gic distributor */
        UINT32 redistBase[LOSCFG_PLATFORM_CPU_NUMBER]; /* the pointer of the gic redistributor */
        UINT32 cpuBase[LOSCFG_PLATFORM_CPU_NUMBER]; /* the base addrees of the gic cpu interface */
    } gic[LOSCFG_PLATFORM_GIC_NUMBER];
} g_gicCb;

/** Macro to access the Distributor Control Register (GICD_CTLR) */
#define GICD_CTLR_RWP                       (1 << 31)
#define GICD_CTLR_E1NWF                     (1 << 7)
#define GICD_CTLR_DS                        (1 << 6)
#define GICD_CTLR_ARE_NS                    (1 << 5)
#define GICD_CTLR_ARE_S                     (1 << 4)
#define GICD_CTLR_ENGRP1S                   (1 << 2)
#define GICD_CTLR_ENGRP1NS                  (1 << 1)
#define GICD_CTLR_ENGRP0                    (1 << 0)

/** Macro to access the Redistributor Control Register (GICR_CTLR) */
#define GICR_CTLR_UWP                       (1 << 31)
#define GICR_CTLR_DPG1S                     (1 << 26)
#define GICR_CTLR_DPG1NS                    (1 << 25)
#define GICR_CTLR_DPG0                      (1 << 24)
#define GICR_CTLR_RWP                       (1 << 3)
#define GICR_CTLR_IR                        (1 << 2)
#define GICR_CTLR_CES                       (1 << 1)
#define GICR_CTLR_EnableLPI                 (1 << 0)

/** Macro to access the Generic Interrupt Controller Interface (GICC) */
#define GIC_CPU_CTRL(hw_base)               __REG32((hw_base) + 0x00U)
#define GIC_CPU_PRIMASK(hw_base)            __REG32((hw_base) + 0x04U)
#define GIC_CPU_BINPOINT(hw_base)           __REG32((hw_base) + 0x08U)
#define GIC_CPU_INTACK(hw_base)             __REG32((hw_base) + 0x0cU)
#define GIC_CPU_EOI(hw_base)                __REG32((hw_base) + 0x10U)
#define GIC_CPU_RUNNINGPRI(hw_base)         __REG32((hw_base) + 0x14U)
#define GIC_CPU_HIGHPRI(hw_base)            __REG32((hw_base) + 0x18U)
#define GIC_CPU_IIDR(hw_base)               __REG32((hw_base) + 0xFCU)

/** Macro to access the Generic Interrupt Controller Distributor (GICD) */
#define GIC_DIST_CTRL(hw_base)              __REG32((hw_base) + 0x000U)
#define GIC_DIST_TYPE(hw_base)              __REG32((hw_base) + 0x004U)
#define GIC_DIST_IGROUP(hw_base, n)         __REG32((hw_base) + 0x080U + ((n)/32U) * 4U)
#define GIC_DIST_ENABLE_SET(hw_base, n)     __REG32((hw_base) + 0x100U + ((n)/32U) * 4U)
#define GIC_DIST_ENABLE_CLEAR(hw_base, n)   __REG32((hw_base) + 0x180U + ((n)/32U) * 4U)
#define GIC_DIST_PENDING_SET(hw_base, n)    __REG32((hw_base) + 0x200U + ((n)/32U) * 4U)
#define GIC_DIST_PENDING_CLEAR(hw_base, n)  __REG32((hw_base) + 0x280U + ((n)/32U) * 4U)
#define GIC_DIST_ACTIVE_SET(hw_base, n)     __REG32((hw_base) + 0x300U + ((n)/32U) * 4U)
#define GIC_DIST_ACTIVE_CLEAR(hw_base, n)   __REG32((hw_base) + 0x380U + ((n)/32U) * 4U)
#define GIC_DIST_PRI(hw_base, n)            __REG32((hw_base) + 0x400U + ((n)/4U) * 4U)
#define GIC_DIST_TARGET(hw_base, n)         __REG32((hw_base) + 0x800U + ((n)/4U) * 4U)
#define GIC_DIST_CONFIG(hw_base, n)         __REG32((hw_base) + 0xc00U + ((n)/16U) * 4U)
#define GIC_DIST_SOFTINT(hw_base)           __REG32((hw_base) + 0xf00U)
#define GIC_DIST_CPENDSGI(hw_base, n)       __REG32((hw_base) + 0xf10U + ((n)/4U) * 4U)
#define GIC_DIST_SPENDSGI(hw_base, n)       __REG32((hw_base) + 0xf20U + ((n)/4U) * 4U)
#define GIC_DIST_ICPIDR2(hw_base)           __REG32((hw_base) + 0xfe8U)
#define GIC_DIST_IROUTER_LOW(hw_base, n)    __REG32((hw_base) + 0x6000U + (n)*8U)
#define GIC_DIST_IROUTER_HIGH(hw_base, n)   __REG32((hw_base) + 0x6000U + (n)*8U + 4)

/* SGI base address  is at 64K offset from Redistributor base address */
#define GIC_RSGI_OFFSET                     0x10000

/** Macro to access the Generic Interrupt Controller Redistributor (GICD) */
#define GIC_RDIST_CTRL(hw_base)              __REG32((hw_base) + 0x000U)
#define GIC_RDIST_IIDR(hw_base)              __REG32((hw_base) + 0x004U)
#define GIC_RDIST_TYPER(hw_base)             __REG32((hw_base) + 0x008U)
#define GIC_RDIST_TSTATUSR(hw_base)          __REG32((hw_base) + 0x010U)
#define GIC_RDIST_WAKER(hw_base)             __REG32((hw_base) + 0x014U)
#define GIC_RDIST_SETLPIR(hw_base)           __REG32((hw_base) + 0x040U)
#define GIC_RDIST_CLRLPIR(hw_base)           __REG32((hw_base) + 0x048U)
#define GIC_RDIST_PROPBASER(hw_base)         __REG32((hw_base) + 0x070U)
#define GIC_RDIST_PENDBASER(hw_base)         __REG32((hw_base) + 0x078U)
#define GIC_RDIST_INVLPIR(hw_base)           __REG32((hw_base) + 0x0A0U)
#define GIC_RDIST_INVALLR(hw_base)           __REG32((hw_base) + 0x0B0U)
#define GIC_RDIST_SYNCR(hw_base)             __REG32((hw_base) + 0x0C0U)

#define GIC_RDISTSGI_IGROUPR0(hw_base, n)    __REG32((hw_base) + GIC_RSGI_OFFSET + 0x080U + (n)*4U)
#define GIC_RDISTSGI_ISENABLER0(hw_base)     __REG32((hw_base) + GIC_RSGI_OFFSET + 0x100U)
#define GIC_RDISTSGI_ICENABLER0(hw_base)     __REG32((hw_base) + GIC_RSGI_OFFSET + 0x180U)
#define GIC_RDISTSGI_ISPENDR0(hw_base)       __REG32((hw_base) + GIC_RSGI_OFFSET + 0x200U)
#define GIC_RDISTSGI_ICPENDR0(hw_base)       __REG32((hw_base) + GIC_RSGI_OFFSET + 0x280U)
#define GIC_RDISTSGI_ISACTIVER0(hw_base)     __REG32((hw_base) + GIC_RSGI_OFFSET + 0x300U)
#define GIC_RDISTSGI_ICACTIVER0(hw_base)     __REG32((hw_base) + GIC_RSGI_OFFSET + 0x380U)
#define GIC_RDISTSGI_IPRIORITYR(hw_base, n)  __REG32((hw_base) + GIC_RSGI_OFFSET + 0x400U + ((n) / 4U) * 4U)
#define GIC_RDISTSGI_ICFGR0(hw_base)         __REG32((hw_base) + GIC_RSGI_OFFSET + 0xC00U)
#define GIC_RDISTSGI_ICFGR1(hw_base)         __REG32((hw_base) + GIC_RSGI_OFFSET + 0xC04U)
#define GIC_RDISTSGI_IGRPMODR0(hw_base, n)   __REG32((hw_base) + GIC_RSGI_OFFSET + 0xD00U + (n)*4)
#define GIC_RDISTSGI_NSACR(hw_base)          __REG32((hw_base) + GIC_RSGI_OFFSET + 0xE00U)


UINT32 HalGicGetActiveIrq(UINT32 gic)
{
    LOS_ASSERT(gic < LOSCFG_PLATFORM_GIC_NUMBER);

    UINT32 irqId;
    GET_ARM_SYS_REG(ICC_IAR1, irqId);
    return (irqId & 0x1FFFFFFUL) + g_gicCb.gic[gic].offset;
}

VOID HalGicIrqAck(UINT32 gic, UINT32 irqId)
{
    LOS_ASSERT(gic < LOSCFG_PLATFORM_GIC_NUMBER);
    LOS_ASSERT(irqId < LOSCFG_PLATFORM_HWI_LIMIT);

    __DSB();
    SET_ARM_SYS_REG(ICC_EOIR1, irqId);
    __ISB();
}


VOID HalGicIrqMask(UINT32 gic, UINT32 irqId)
{
    LOS_ASSERT(gic < LOSCFG_PLATFORM_GIC_NUMBER);
    LOS_ASSERT(irqId < LOSCFG_PLATFORM_HWI_LIMIT);

    irqId = irqId - g_gicCb.gic[gic].offset;
    if (irqId >= 32) {
        GIC_DIST_ENABLE_CLEAR(g_gicCb.gic[gic].distBase, irqId) = 1UL << (irqId % 32);
    } else {
        GIC_RDISTSGI_ICENABLER0(g_gicCb.gic[gic].redistBase[g_gicCb.cpuId]) = 1UL << (irqId % 32);
    }
    __DSB();
    __ISB();
    __DMB();
}

VOID HalGicIrqUmask(UINT32 gic, UINT32 irqId)
{
    LOS_ASSERT(gic < LOSCFG_PLATFORM_GIC_NUMBER);
    LOS_ASSERT(irqId < LOSCFG_PLATFORM_HWI_LIMIT);

    irqId = irqId - g_gicCb.gic[gic].offset;
    if (irqId >= 32) {
        GIC_DIST_ENABLE_SET(g_gicCb.gic[gic].distBase, irqId) = 1UL << (irqId % 32);
    } else {
        GIC_RDISTSGI_ISENABLER0(g_gicCb.gic[gic].redistBase[g_gicCb.cpuId]) = 1UL << (irqId % 32);
    }
    __DSB();
    __ISB();
    __DMB();
}


VOID HalGicBroadcastSgi(UINT32 gic, UINT32 irqId)
{
    UINT64 sgiVal = (1ULL << 40) | ((irqId & 0xF) << 24);

    __DSB();
    SET_ARM_SYS_REG64(ICC_SGI1R, sgiVal);
    __ISB();
}

VOID HalGicSinglecastSgi(UINT32 gic, UINT32 irqId, UINT32 cpuId)
{
    UINT64 sgiVal = ((irqId & 0xF) << 24) | ((1 << cpuId) & 0xFF);
    UINT64 affinity = (UINT64) HalGetAffinityFromCpuId(cpuId);

    sgiVal |= ((affinity >> 8) & 0xFF) << 16;
    sgiVal |= ((affinity >> 16) & 0xFF) << 32;
    sgiVal |= ((affinity >> 24) & 0xFF) << 48;

    __DSB();
    SET_ARM_SYS_REG64(ICC_SGI1R, sgiVal);
    __ISB();
}

VOID HalGicMulticastSgi(UINT32 gic, UINT32 irqId, UINT32 targetList)
{
    UINT64 sgiVal = 0;
    UINT64 affinity = 0;

    for (UINT8 i = 0; i < 16; i++) {
        if (targetList & (1 << i)) {
            sgiVal = ((irqId & 0xF) << 24) | ((1 << i) & 0xFF);
            affinity = (UINT64) HalGetAffinityFromCpuId(i);
            sgiVal |= ((affinity >> 8) & 0xFF) << 16;
            sgiVal |= ((affinity >> 16) & 0xFF) << 32;
            sgiVal |= ((affinity >> 24) & 0xFF) << 48;
        }

        __DSB();
        SET_ARM_SYS_REG64(ICC_SGI1R, sgiVal);
        __ISB();
    }
}


VOID HalGicSetPendingIrq(UINT32 gic, UINT32 irqId)
{
    LOS_ASSERT(gic < LOSCFG_PLATFORM_GIC_NUMBER);
    LOS_ASSERT(irqId < LOSCFG_PLATFORM_HWI_LIMIT);

    irqId = irqId - g_gicCb.gic[gic].offset;
    if (irqId >= 32) {
        GIC_DIST_PENDING_SET(g_gicCb.gic[gic].distBase, irqId) = 1UL << (irqId % 32);
    } else {
        GIC_RDISTSGI_ISPENDR0(g_gicCb.gic[gic].redistBase[g_gicCb.cpuId]) = 1UL << (irqId % 32);
    }
    __DSB();
    __ISB();
    __DMB();
}

UINT32 HalGicGetPendingIrq(UINT32 gic, UINT32 irqId)
{
    LOS_ASSERT(gic < LOSCFG_PLATFORM_GIC_NUMBER);
    LOS_ASSERT(irqId < LOSCFG_PLATFORM_HWI_LIMIT);

    irqId = irqId - g_gicCb.gic[gic].offset;
    if (irqId >= 32) {
        return (GIC_DIST_PENDING_SET(g_gicCb.gic[gic].distBase, irqId) >> (irqId % 32)) & 1UL;
    } else {
        return (GIC_RDISTSGI_ICPENDR0(g_gicCb.gic[gic].redistBase[g_gicCb.cpuId]) >> (irqId % 32)) & 1UL;
    }
}

VOID HalGicClearPendingIrq(UINT32 gic, UINT32 irqId)
{
    LOS_ASSERT(gic < LOSCFG_PLATFORM_GIC_NUMBER);
    LOS_ASSERT(irqId < LOSCFG_PLATFORM_HWI_LIMIT);

    irqId = irqId - g_gicCb.gic[gic].offset;
    if (irqId >= 32) {
        GIC_DIST_PENDING_CLEAR(g_gicCb.gic[gic].distBase, irqId) = 1UL << (irqId % 32);
    } else {
        GIC_RDISTSGI_ISPENDR0(g_gicCb.gic[gic].redistBase[g_gicCb.cpuId]) =  1UL << (irqId % 32);
    }
    __DSB();
    __ISB();
    __DMB();
}


VOID HalGicSetIrqConfig(UINT32 gic, UINT32 irqId, UINT32 config)
{
    LOS_ASSERT(gic < LOSCFG_PLATFORM_GIC_NUMBER);
    LOS_ASSERT(irqId < LOSCFG_PLATFORM_HWI_LIMIT);

    irqId = irqId - g_gicCb.gic[gic].offset;

    UINT32 icfgr = GIC_DIST_CONFIG(g_gicCb.gic[gic].distBase, irqId);
    UINT32 shift = (irqId % 16U) << 1U;

    icfgr &= (~(3U << shift));
    icfgr |= (config << shift);
    GIC_DIST_CONFIG(g_gicCb.gic[gic].distBase, irqId) = icfgr;
}

UINT32 HalGicIrqGetConfig(UINT32 gic, UINT32 irqId)
{
    LOS_ASSERT(gic < LOSCFG_PLATFORM_GIC_NUMBER);
    LOS_ASSERT(irqId < LOSCFG_PLATFORM_HWI_LIMIT);

    irqId = irqId - g_gicCb.gic[gic].offset;
    return (GIC_DIST_CONFIG(g_gicCb.gic[gic].distBase, irqId) >> ((irqId % 16U) >> 1U));
}


VOID HalGicIrqSetTargetCpu(UINT32 gic, UINT32 irqId, UINT32 cpuMask)
{
    LOS_ASSERT(gic < LOSCFG_PLATFORM_GIC_NUMBER);
    LOS_ASSERT(irqId < LOSCFG_PLATFORM_HWI_LIMIT);

    irqId = irqId - g_gicCb.gic[gic].offset;

    UINT32 target = GIC_DIST_TARGET(g_gicCb.gic[gic].distBase, irqId);
    target &= ~(0x0FFUL << ((irqId % 4U) * 8U));
    target |= cpuMask << ((irqId % 4U) * 8U);
    GIC_DIST_TARGET(g_gicCb.gic[gic].distBase, irqId) = target;
}

UINT32 HalGicIrqGetTargetCpu(UINT32 gic, UINT32 irqId)
{
    LOS_ASSERT(gic < LOSCFG_PLATFORM_GIC_NUMBER);
    LOS_ASSERT(irqId < LOSCFG_PLATFORM_HWI_LIMIT);

    irqId = irqId - g_gicCb.gic[gic].offset;
    return (GIC_DIST_TARGET(g_gicCb.gic[gic].distBase, irqId) >> ((irqId % 4U) * 8U)) & 0xFFUL;
}


VOID HalGicSetIrqPriority(UINT32 gic, UINT32 irqId, UINT8 priority)
{
    LOS_ASSERT(gic < LOSCFG_PLATFORM_GIC_NUMBER);
    LOS_ASSERT(irqId < LOSCFG_PLATFORM_HWI_LIMIT);
    LOS_ASSERT(priority < (1 << g_gicCb.priOft));

    UINT32 mask;
    irqId = irqId - g_gicCb.gic[gic].offset;
    priority = priority << g_gicCb.priOft;
    if (irqId >= 32) {
        mask = GIC_DIST_PRI(g_gicCb.gic[gic].distBase, irqId);
    } else {
        mask = GIC_RDISTSGI_IPRIORITYR(g_gicCb.gic[gic].redistBase[g_gicCb.cpuId], irqId);
    }
    mask &= ~(0xFFUL << ((irqId % 4) * 8));
    mask |= ((UINT32) priority << ((irqId % 4) * 8));
    if (irqId >= 32) {
        GIC_DIST_PRI(g_gicCb.gic[gic].distBase, irqId) = mask;
    } else {
        GIC_RDISTSGI_IPRIORITYR(g_gicCb.gic[gic].redistBase[g_gicCb.cpuId], irqId) = mask;
    }
}

UINT8 HalGicGetIrqPriority(UINT32 gic, UINT32 irqId)
{
    LOS_ASSERT(gic < LOSCFG_PLATFORM_GIC_NUMBER);
    LOS_ASSERT(irqId < LOSCFG_PLATFORM_HWI_LIMIT);

    UINT32 mask;
    irqId = irqId - g_gicCb.gic[gic].offset;
    if (irqId >= 32) {
        mask = GIC_DIST_PRI(g_gicCb.gic[gic].distBase, irqId);
    } else {
        mask = GIC_RDISTSGI_IPRIORITYR(g_gicCb.gic[gic].redistBase[g_gicCb.cpuId], irqId);
    }
    return (UINT8) (((mask >> ((irqId % 4) * 8)) & 0xff) >> g_gicCb.priOft);
}


VOID HalGicSetInterfacePriorityMask(UINT32 gic, UINT8 priority)
{
    LOS_ASSERT(gic < LOSCFG_PLATFORM_GIC_NUMBER);

    UINT32 mask = (UINT32) (priority << g_gicCb.priOft);
    SET_ARM_SYS_REG(ICC_PMR, mask);
}

UINT8 HalGicGetInterfacePriorityMask(UINT32 gic)
{
    LOS_ASSERT(gic < LOSCFG_PLATFORM_GIC_NUMBER);

    UINT32 mask;
    GET_ARM_SYS_REG(ICC_PMR, mask);
    return (UINT8) ((mask & 0xFF) >> g_gicCb.priOft);
}


VOID HalGicSetBinaryPoint(UINT32 gic, UINT32 binaryPoint)
{
    (VOID)gic;
    binaryPoint &= 0x7UL;
    SET_ARM_SYS_REG(ICC_BPR1, binaryPoint);
}

UINT32 HalGicGetBinaryPoint(UINT32 gic)
{
    (VOID)gic;
    UINT32 mask;
    GET_ARM_SYS_REG(ICC_BPR1, mask);
    return (mask & 0x7UL);
}


VOID HalGicSetGroup(UINT32 gic, UINT32 irqId, UINT32 group)
{
    LOS_ASSERT(gic < LOSCFG_PLATFORM_GIC_NUMBER);
    LOS_ASSERT(irqId < LOSCFG_PLATFORM_HWI_LIMIT);
    LOS_ASSERT(group <= 1U);

    irqId = irqId - g_gicCb.gic[gic].offset;
    UINT32 igroupr = GIC_DIST_IGROUP(g_gicCb.gic[gic].distBase, irqId);
    UINT32 shift = (irqId % 32U);

    igroupr &= (~(1U << shift));
    igroupr |= ((group & 0x1U) << shift);
    GIC_DIST_IGROUP(g_gicCb.gic[gic].distBase, irqId) = igroupr;
}

UINT32 HalGicGetGroup(UINT32 gic, UINT32 irqId)
{
    LOS_ASSERT(gic < LOSCFG_PLATFORM_GIC_NUMBER);
    LOS_ASSERT(irqId < LOSCFG_PLATFORM_HWI_LIMIT);

    irqId = irqId - g_gicCb.gic[gic].offset;
    return (GIC_DIST_IGROUP(g_gicCb.gic[gic].distBase, irqId) >> (irqId % 32U)) & 0x1UL;
}


UINT32 HalGicGetIrqStatus(UINT32 gic, UINT32 irqId)
{
    LOS_ASSERT(gic < LOSCFG_PLATFORM_GIC_NUMBER);
    LOS_ASSERT(irqId < LOSCFG_PLATFORM_HWI_LIMIT);

    irqId = irqId - g_gicCb.gic[gic].offset;
    UINT32 active = (GIC_DIST_ACTIVE_SET(g_gicCb.gic[gic].distBase, irqId) >> (irqId % 32U)) & 0x1UL;
    UINT32 pending = (GIC_DIST_PENDING_SET(g_gicCb.gic[gic].distBase, irqId) >> (irqId % 32U)) & 0x1UL;
    return (active << 1U) | pending;
}

VOID HalGicClearActiveIrq(UINT32 gic, UINT32 irqId)
{
    LOS_ASSERT(gic < LOSCFG_PLATFORM_GIC_NUMBER);
    LOS_ASSERT(irqId < LOSCFG_PLATFORM_HWI_LIMIT);

    irqId = irqId - g_gicCb.gic[gic].offset;
    GIC_DIST_ACTIVE_CLEAR(g_gicCb.gic[gic].distBase, irqId) = 1U << (irqId % 32U);
}


UINT32 HalGicGetHighPendingIrq(UINT32 gic)
{
    (VOID)gic;
    UINT32 irqId;
    GET_ARM_SYS_REG(ICC_HPPIR1, irqId);
    return irqId;
}


UINT32 HalGicGetInterfaceId(UINT32 gic)
{
    LOS_ASSERT(gic < LOSCFG_PLATFORM_GIC_NUMBER);

    return GIC_CPU_IIDR(g_gicCb.gic[gic].cpuBase[g_gicCb.cpuId]);
}


UINT32 HalGicGetMaxIrqNum(VOID)
{
    return g_gicCb.maxIrq;
}


STATIC VOID HalGicWaitRwp(UINT32 gic, UINT32 irqId)
{
    LOS_ASSERT(gic < LOSCFG_PLATFORM_GIC_NUMBER);
    LOS_ASSERT(irqId < LOSCFG_PLATFORM_HWI_LIMIT);

    UINT32 base, rwpBit;
    if (irqId < 32) {
        base = g_gicCb.gic[gic].redistBase[g_gicCb.cpuId];
        rwpBit = GICR_CTLR_RWP;
    } else {
        base = g_gicCb.gic[gic].distBase;
        rwpBit = GICD_CTLR_RWP;
    }

    while (__REG32(base) & rwpBit) {}
}

STATIC VOID HalGicDistributorInit(UINT32 gic, UINT32 distributorBase, UINT32 startIrqId)
{
    LOS_ASSERT(gic < LOSCFG_PLATFORM_GIC_NUMBER);

    g_gicCb.gic[gic].distBase = distributorBase;
    g_gicCb.gic[gic].offset = startIrqId;

    /* 获取支持的中断个数. */
    UINT32 gicType = GIC_DIST_TYPE(g_gicCb.gic[gic].distBase);
    g_gicCb.maxIrq = (UINT16) (((gicType & 0x1f) + 1) * 32);

    /*
     * The GIC only supports up to 1020 interrupt sources.
     * Limit this to either the architected maximum, or the
     * platform maximum.
     */
    if (g_gicCb.maxIrq > 1020) {
        g_gicCb.maxIrq = 1020;
    }
    /* the platform maximum interrupts */
    if (g_gicCb.maxIrq > LOSCFG_PLATFORM_HWI_LIMIT) {
        g_gicCb.maxIrq = LOSCFG_PLATFORM_HWI_LIMIT;
    }

    GIC_DIST_CTRL(g_gicCb.gic[gic].distBase) = 0x0UL;
    /* Wait for register write pending. */
    HalGicWaitRwp(gic, 32);
    /* Set all global interrupts to be level triggered, active low. */
    for (UINT32 i = 32; i < g_gicCb.maxIrq; i += 16)
        GIC_DIST_CONFIG(g_gicCb.gic[gic].distBase, i) = 0x0UL;

    UINT32 affinity;
    GET_ARM_SYS_REG(MPIDR, affinity);
    /* aff2[23:16], aff1[15:8], aff0[7:0] */
    affinity &= 0x00FFFFFFUL;
    /* Wait for register write pending. */
    HalGicWaitRwp(gic, 32);
    /* Set all global interrupts to this CPU only. */
    for (UINT32 i = 32; i < g_gicCb.maxIrq; i++) {
        GIC_DIST_IROUTER_LOW(g_gicCb.gic[gic].distBase, i) = affinity;
        GIC_DIST_IROUTER_HIGH(g_gicCb.gic[gic].distBase, i) = 0x0UL;
    }

    /* Wait for register write pending. */
    HalGicWaitRwp(gic, 32);
    /* Set priority on spi interrupts. */
    for (UINT32 i = 32U; i < g_gicCb.maxIrq; i += 4U) {
        GIC_DIST_PRI(g_gicCb.gic[gic].distBase, i) = 0xa0a0a0a0U;
    }

    /* Wait for register write pending. */
    HalGicWaitRwp(gic, 32);
    /* Disable all interrupts. */
    for (UINT32 i = 0U; i < g_gicCb.maxIrq; i += 32U) {
        GIC_DIST_PENDING_CLEAR(g_gicCb.gic[gic].distBase, i) = 0xffffffffU;
        GIC_DIST_ENABLE_CLEAR(g_gicCb.gic[gic].distBase, i) = 0xffffffffU;
    }

    /* Wait for register write pending. */
    HalGicWaitRwp(gic, 32);
    /* All interrupts defaults to IGROUP1(IRQ). */
    for (UINT32 i = 0U; i < g_gicCb.maxIrq; i += 32U) {
        GIC_DIST_IGROUP(g_gicCb.gic[gic].distBase, i) = 0xffffffffU;
    }

    /* Wait for register write pending. */
    HalGicWaitRwp(gic, 32);
    /*
     * The Distributor control register (GICD_CTLR) must be configured to enable the interrupt groups and to set the routing mode.
     * Enable Affinity routing (ARE bits) The ARE bits in GICD_CTLR control whether affinity routing is enabled.
     * If affinity routing is not enabled, GICv3 can be configured for legacy operation.
     * Whether affinity routing is enabled or not can be controlled separately for Secure and Non-secure state.
     * Enables GICD_CTLR contains separate enable bits for Group 0, Secure Group 1 and Non-secure Group 1:
     * GICD_CTLR.EnableGrp1S enables distribution of Secure Group 1 interrupts.
     * GICD_CTLR.EnableGrp1NS enables distribution of Non-secure Group 1 interrupts.
     * GICD_CTLR.EnableGrp0 enables distribution of Group 0 interrupts.
     */
    GIC_DIST_CTRL(g_gicCb.gic[gic].distBase) = GICD_CTLR_ARE_NS | GICD_CTLR_ENGRP1NS;
}

STATIC VOID HalGicRedistributorInit(UINT32 gic, UINT32 redistributorBase)
{
    LOS_ASSERT(gic < LOSCFG_PLATFORM_GIC_NUMBER);

    redistributorBase += (g_gicCb.cpuId + LOSCFG_PLATFORM_GIC_REDIST_OFFSET) * (2 << 16);
    g_gicCb.gic[gic].redistBase[g_gicCb.cpuId] = redistributorBase;

    /* redistributor enable */
    GIC_RDIST_WAKER(g_gicCb.gic[gic].redistBase[g_gicCb.cpuId]) &= ~(1U << 1);
    while (GIC_RDIST_WAKER(g_gicCb.gic[gic].redistBase[g_gicCb.cpuId]) & (1 << 2)) {}
    /* Disable all sgi and ppi interrupt */
    GIC_RDISTSGI_ICENABLER0(g_gicCb.gic[gic].redistBase[g_gicCb.cpuId]) = 0xFFFFFFFF;
    /* Wait for register write pending. */
    HalGicWaitRwp(gic, 0);
    /* Clear all inetrrupt pending */
    GIC_RDISTSGI_ICPENDR0(g_gicCb.gic[gic].redistBase[g_gicCb.cpuId]) = 0xFFFFFFFF;
    /* the corresponding interrupt is Group 1 or Non-secure Group 1. */
    GIC_RDISTSGI_IGROUPR0(g_gicCb.gic[gic].redistBase[g_gicCb.cpuId], 0) = 0xFFFFFFFF;
    GIC_RDISTSGI_IGRPMODR0(g_gicCb.gic[gic].redistBase[g_gicCb.cpuId], 0) = 0xFFFFFFFF;
    /* Configure default priorities for SGI 0:15 and PPI 16:31. */
    for (UINT32 i = 0; i < 32; i += 4) {
        GIC_RDISTSGI_IPRIORITYR(g_gicCb.gic[gic].redistBase[g_gicCb.cpuId], i) = 0xa0a0a0a0U;
    }
    /* Trigger level for PPI interrupts*/
    GIC_RDISTSGI_ICFGR1(g_gicCb.gic[gic].redistBase[g_gicCb.cpuId]) = 0x0U; /* PPI is level-sensitive. */
}

STATIC VOID HalGicCpuInterfaceInit(UINT32 gic, UINT32 cpuInterfaceBase)
{
    LOS_ASSERT(gic < LOSCFG_PLATFORM_GIC_NUMBER);

    UINT32 value;

    g_gicCb.gic[gic].cpuBase[g_gicCb.cpuId] = cpuInterfaceBase;

    GET_ARM_SYS_REG(ICC_SRE, value);
    value |= (1U << 0);
    value &= 0xFFUL;
    SET_ARM_SYS_REG(ICC_SRE, value);
    __ISB();

    /* 获取实现的优先级位数. */
    GET_ARM_SYS_REG(ICC_CTLR, value);
    g_gicCb.priOft = (UINT16) ((value >> 8) & 0x7);

    SET_ARM_SYS_REG(ICC_CTLR, 0);

    HalGicSetInterfacePriorityMask(gic, (0xFF >> g_gicCb.priOft));

    /* Enable group1 interrupt */
    SET_ARM_SYS_REG(ICC_IGRPEN1, 1);

    HalGicSetBinaryPoint(0, 0);

    /* ICC_BPR0_EL1 determines the preemption group for both Group 0 and Group 1 interrupts. */
    SET_ARM_SYS_REG(ICC_CTLR, 1);
}

VOID HalGicInit(UINT32 gic, UINT32 startIrqId)
{
    STATIC BOOL gicInitFlag = FALSE;
    if (gicInitFlag == TRUE) {
        return;
    }

    g_gicCb.cpuId = HalGetCpuId();
    LOS_ASSERT(g_gicCb.cpuId < LOSCFG_PLATFORM_CPU_NUMBER);

    HalGicDistributorInit(gic, LOSCFG_PLATFORM_GIC_DIST_BASE_ADDR, startIrqId);
    HalGicRedistributorInit(gic, LOSCFG_PLATFORM_GIC_REDIST_BASE_ADDR);
    HalGicCpuInterfaceInit(gic, LOSCFG_PLATFORM_GIC_CPU_BASE_ADDR);
    gicInitFlag = TRUE;

}

VOID HalGicDumpType(UINT32 gic)
{
    UINT32 gic_type = GIC_DIST_TYPE(g_gicCb.gic[gic].distBase);
    PRINTK("GICv%d on %p, max IRQs: %d, %s security extension(%08x)\n",
           (GIC_DIST_ICPIDR2(g_gicCb.gic[gic].distBase) >> 4U) & 0xfUL,
           g_gicCb.gic[gic].distBase,
           g_gicCb.maxIrq,
           gic_type & (1U << 10U) ? "has" : "no",
           gic_type);
}

VOID HalGicDump(UINT32 gic)
{
    UINT32 k = HalGicGetHighPendingIrq(gic);
    PRINTK("--- high pending priority: %d(%08x)\n", k, k);

    PRINTK("--- hw mask ---\n");
    for (UINT32 i = 0U; i < g_gicCb.maxIrq / 32U; i++) {
        PRINTK("0x%08x, ", GIC_DIST_ENABLE_SET(g_gicCb.gic[gic].distBase, i * 32U));
    }

    PRINTK("\n--- hw pending ---\n");
    for (UINT32 i = 0U; i < g_gicCb.maxIrq / 32U; i++) {
        PRINTK("0x%08x, ", GIC_DIST_PENDING_SET(g_gicCb.gic[gic].distBase, i * 32U));
    }

    PRINTK("\n--- hw active ---\n");
    for (UINT32 i = 0U; i < g_gicCb.maxIrq / 32U; i++) {
        PRINTK("0x%08x, ", GIC_DIST_ACTIVE_SET(g_gicCb.gic[gic].distBase, i * 32U));
    }
    PRINTK("\n");
}

#endif /* LOSCFG_PLATFORM_GIC_VERSION */
